﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
using Rhino.Mocks;
using NUnit.Framework;
using System.Threading;
using System.Web;
using System.Web.Caching;

namespace mForum.Web.Common.Tests.Infrastructure.AssetManagerTests
{
    using mForum.Core.Common;
    using mForum.Core.Infrastructure;
    using mForum.Web.Common.Infrastructure;
    using mForum.Web.Common.Infrastructure.AssetManager;
    using mForum.Web.Common.Infrastructure.Cache;

    [TestFixture]
    public class AssetManagerTests
    {
        private IHttpContextProvider _httpContextProvider;
        private HttpResponseBase _httpResponse;
        private HttpRequestBase _httpRequest;
        private HttpContextBase _httpContext;
        private ICache _cache;
        private IFileWrapper _fileWrapper;
        private IAssetManager _assetManager;
        private IVirtualPathUtility _virtualPathUtility;

        private Hashtable _items;

        [SetUp]
        public void SetUp()
        {
            _items = new Hashtable();

            _httpContextProvider = MockRepository.GenerateStub<IHttpContextProvider>();
            _httpContext = MockRepository.GenerateStub<HttpContextBase>();
            _httpRequest = MockRepository.GenerateStub<HttpRequestBase>();
            _httpResponse = MockRepository.GenerateStub<HttpResponseBase>();
            _virtualPathUtility = MockRepository.GenerateStub<IVirtualPathUtility>();

            _httpContext.Stub(c => c.Items).Return(_items);

            _httpContextProvider.Stub(h => h.Context).Return(_httpContext);
            _httpContextProvider.Stub(h => h.Response).Return(_httpResponse);
            _httpContextProvider.Stub(H => H.Request).Return(_httpRequest);

            _httpContextProvider.Stub(h => h.Context.Request.MapPath(null)).IgnoreArguments().Do((Func<string, string>)(path => path));

            _cache = new Cache(HttpRuntime.Cache);
            _fileWrapper = MockRepository.GenerateMock<IFileWrapper>();

            _assetManager = new AssetManager(_httpContextProvider, _cache, _fileWrapper, _virtualPathUtility);
        }

        [Test]
        public void can_get_asset_reference()
        {
            DateTime modificationDate = DateTime.Now;
            string assetType = AssetType.Script;
            string assetCommonCacheKey = "Assets_Script_Common";
            string assetViewSpecificCacheKey = "Assets_Script_ViewSpecific";

            List<AssetItem> assetItems = GenerateFakeAssetItemList();

            foreach (var assetItem in assetItems)
            {
                _assetManager.RegisterAsset(assetItem.Url, assetItem.Type, assetItem.Category);
            }

            _fileWrapper.Stub(f => f.GetLastWriteTime(Arg<string>.Is.Anything)).Return(modificationDate);
            _fileWrapper.Stub(f => f.GetLastWriteTime(AssetType.GetBasePathForType(assetType) + assetItems[0].Url)).Return(modificationDate.AddHours(-1));
            _fileWrapper.Stub(f => f.GetLastWriteTime(AssetType.GetBasePathForType(assetType) + assetItems[1].Url)).Return(modificationDate.AddMonths(-3));

            _virtualPathUtility.Stub(v => v.ToAbsolute(Arg<string>.Is.Anything)).Return("/Script.axd");

            string expectedResult = String.Format("<script src=\"/Script.axd?d={0}&t=Common\" type=\"text/javascript\"></script>", modificationDate);
            expectedResult += String.Format("<script src=\"/Script.axd?d={0}&t=ViewSpecific\" type=\"text/javascript\"></script>", modificationDate);

            string result = _assetManager.GetAssetReference(assetType);

            Assert.AreEqual(expectedResult, result);

            Assert.AreEqual(2, _cache.Get<List<AssetItem>>(assetCommonCacheKey).Count);
            Assert.AreEqual(1, _cache.Get<List<AssetItem>>(assetViewSpecificCacheKey).Count);

        }

        [Test]
        public void can_substitute_assets_placeholders()
        {
            string template = "<html><head>";
            string expectedResult = template;

            IAssetManager assetManager = MockRepository.GenerateStub<IAssetManager>();
            foreach (var type in AssetType.AssetTypeDictionary)
            {
                assetManager.Stub(a => a.GetAssetReference(type.Key)).Return(type.Key.ToUpper());
                template += String.Format("#{0}_REFERENCE#", type.Key.ToUpper());
                expectedResult += type.Key.ToUpper();
            }

            template += "</head></html>";
            expectedResult += "</head></html>";

            _httpResponse.ContentType = "text/html";

            MemoryStream outputStream = new MemoryStream();
            PostprocessingModule.SubstitutionStream postprocessingModule = new PostprocessingModule.SubstitutionStream(outputStream, _httpContextProvider, assetManager);

            byte[] templateBytes = Encoding.UTF8.GetBytes(template);
            postprocessingModule.Write(templateBytes,0, template.Length);
            postprocessingModule.Flush();

            byte[] outputBytes = outputStream.ToArray();
            string output = Encoding.UTF8.GetString(outputBytes);

            Assert.AreEqual(expectedResult, output);
        }

        private List<AssetItem> GenerateFakeAssetItemList()
        {
            List<AssetItem> result = new List<AssetItem>();

            result.Add(new AssetItem()
            {
                Type = AssetType.Script,
                Category = AssetCategory.Common,
                Url = "test.js"
            });

            result.Add(new AssetItem()
            {
                Type = AssetType.Script,
                Category = AssetCategory.Common,
                Url = "test1.js"
            });

            result.Add(new AssetItem()
            {
                Type = AssetType.Script,
                Category = AssetCategory.ViewSpecific,
                Url = "test2.js"
            });

            result.Add(new AssetItem()
            {
                Type = AssetType.Stylesheet,
                Category = AssetCategory.Common,
                Url = "test.css"
            });

            result.Add(new AssetItem()
            {
                Type = AssetType.Stylesheet,
                Category = AssetCategory.ViewSpecific,
                Url = "test1.css"
            });

            result.Add(new AssetItem()
            {
                Type = AssetType.Stylesheet,
                Category = AssetCategory.ViewSpecific,
                Url = "test2.css"
            });

            return result;
        }
    }
}
